{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cedad359-37d8-46d9-a308-3e2f1bf8f495",
   "metadata": {},
   "source": [
    "# Google combined tools example\n",
    "\n",
    "This notebook features a more advanced usage of Agent Tools, using the Google Calendar, Mail and Search integrations as well as the Load and Search Meta Tool to fufill a more complicated set of tasks for the user.\n",
    "\n",
    "## Setup the Tools\n",
    "First we will import OpenAI and setup the Agent:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d71da502-14cc-485a-a816-46f69c0282ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"sk-api-key\"\n",
    "\n",
    "from llama_index.core.agent.workflow import FunctionAgent\n",
    "from llama_index.llms.openai import OpenAI"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2344bd4e-7c50-4c30-ad55-ba30ea7f2edf",
   "metadata": {},
   "source": [
    "Now we can import the Google Tools we are going to use. See the README for the respective tools to get started with authentication."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0bc67df-25ea-4d81-8e10-912fa59d9d75",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.tools.gmail.base import GmailToolSpec\n",
    "from llama_index.tools.google_calendar.base import GoogleCalendarToolSpec\n",
    "from llama_index.tools.google_search.base import GoogleSearchToolSpec\n",
    "\n",
    "gmail_tools = GmailToolSpec().to_tool_list()\n",
    "gcal_tools = GoogleCalendarToolSpec().to_tool_list()\n",
    "gsearch_tools = GoogleSearchToolSpec(key=\"api-key\", engine=\"engine\").to_tool_list()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e1f852e-7f96-405e-bf74-de7c561aa024",
   "metadata": {},
   "source": [
    "Let's take a look at all of the tools we have available from the 3 tool specs we initialized:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca6c5d85-4779-4011-8253-9cd0d6814cb8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load_data\n",
      "load_data() -> List[llama_index.schema.Document]\n",
      "Load emails from the user's account\n",
      "search_messages\n",
      "search_messages()\n",
      "\n",
      "create_draft\n",
      "create_draft(to: Optional[List[str]] = None, subject: Optional[str] = None, message: Optional[str] = None) -> str\n",
      "Create and insert a draft email.\n",
      "           Print the returned draft's message and id.\n",
      "           Returns: Draft object, including draft id and message meta data.\n",
      "\n",
      "        Args:\n",
      "            to (Optional[str]): The email addresses to send the message to\n",
      "            subject (Optional[str]): The subject for the event\n",
      "            message (Optional[str]): The message for the event\n",
      "        \n",
      "update_draft\n",
      "update_draft(to: Optional[List[str]] = None, subject: Optional[str] = None, message: Optional[str] = None, draft_id: str = None) -> str\n",
      "Update a draft email.\n",
      "           Print the returned draft's message and id.\n",
      "           This function is required to be passed a draft_id that is obtained when creating messages\n",
      "           Returns: Draft object, including draft id and message meta data.\n",
      "\n",
      "        Args:\n",
      "            to (Optional[str]): The email addresses to send the message to\n",
      "            subject (Optional[str]): The subject for the event\n",
      "            message (Optional[str]): The message for the event\n",
      "            draft_id (str): the id of the draft to be updated\n",
      "        \n",
      "get_draft\n",
      "get_draft(draft_id: str = None) -> str\n",
      "Get a draft email.\n",
      "           Print the returned draft's message and id.\n",
      "           Returns: Draft object, including draft id and message meta data.\n",
      "\n",
      "        Args:\n",
      "            draft_id (str): the id of the draft to be updated\n",
      "        \n",
      "send_draft\n",
      "send_draft(draft_id: str = None) -> str\n",
      "Sends a draft email.\n",
      "           Print the returned draft's message and id.\n",
      "           Returns: Draft object, including draft id and message meta data.\n",
      "\n",
      "        Args:\n",
      "            draft_id (str): the id of the draft to be updated\n",
      "        \n",
      "load_data\n",
      "load_data(number_of_results: Optional[int] = 100, start_date: Union[str, datetime.date, NoneType] = None) -> List[llama_index.schema.Document]\n",
      "Load data from user's calendar.\n",
      "\n",
      "        Args:\n",
      "            number_of_results (Optional[int]): the number of events to return. Defaults to 100.\n",
      "            start_date (Optional[Union[str, datetime.date]]): the start date to return events from in date isoformat. Defaults to today.\n",
      "        \n",
      "create_event\n",
      "create_event(title: Optional[str] = None, description: Optional[str] = None, location: Optional[str] = None, start_datetime: Union[str, datetime.datetime, NoneType] = None, end_datetime: Union[str, datetime.datetime, NoneType] = None, attendees: Optional[List[str]] = None) -> str\n",
      "\n",
      "            Create an event on the users calendar\n",
      "\n",
      "        Args:\n",
      "            title (Optional[str]): The title for the event\n",
      "            description (Optional[str]): The description for the event\n",
      "            location (Optional[str]): The location for the event\n",
      "            start_datetime Optional[Union[str, datetime.datetime]]: The start datetime for the event\n",
      "            end_datetime Optional[Union[str, datetime.datetime]]: The end datetime for the event\n",
      "            attendees Optional[List[str]]: A list of email address to invite to the event\n",
      "        \n",
      "get_date\n",
      "get_date()\n",
      "\n",
      "        A function to return todays date. Call this before any other functions if you are unaware of the date\n",
      "        \n",
      "google_search\n",
      "google_search(query: str)\n",
      "\n",
      "        Make a query the google search engine to receive a list of results.\n",
      "\n",
      "        Args:\n",
      "            query (str): The query to be passed to google search.\n",
      "\n",
      "        \n"
     ]
    }
   ],
   "source": [
    "for tool in [*gmail_tools, *gcal_tools, *gsearch_tools]:\n",
    "    print(tool.metadata.name)\n",
    "    print(tool.metadata.description)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c514ae53-5871-46c3-9759-d82f4be0d84e",
   "metadata": {},
   "source": [
    "We have to be conscious of the models context length when using these tools as if we are not careful the response can easily be larger than the token limit. In particular, the load_data function for emails returns large payloads, as well as google search. In this example I will wrap those two tools in the Load and Search Meta tool:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a3e3309-b499-46b7-9b29-b61c520ae257",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.tools.tool_spec.load_and_search.base import LoadAndSearchToolSpec\n",
    "\n",
    "print(\"Wrapping \" + gsearch_tools[0].metadata.name)\n",
    "gsearch_load_and_search_tools = LoadAndSearchToolSpec.from_defaults(\n",
    "    gsearch_tools[0],\n",
    ").to_tool_list()\n",
    "\n",
    "print(\"Wrapping gmail \" + gmail_tools[0].metadata.name)\n",
    "gmail_load_and_search_tools = LoadAndSearchToolSpec.from_defaults(\n",
    "    gmail_tools[0],\n",
    ").to_tool_list()\n",
    "\n",
    "print(\"Wrapping google calendar \" + gcal_tools[0].metadata.name)\n",
    "gcal_load_and_search_tools = LoadAndSearchToolSpec.from_defaults(\n",
    "    gcal_tools[0],\n",
    ").to_tool_list()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc206b48-469c-4e5d-a403-7414b38b3b9d",
   "metadata": {},
   "source": [
    "Notice we are only wrapping individual tools out of the tool list. Lets combine the all the tools together into a combined list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7eba0f02-f202-4295-8f15-f23569830a83",
   "metadata": {},
   "outputs": [],
   "source": [
    "all_tools = [\n",
    "    *gsearch_load_and_search_tools,\n",
    "    *gmail_load_and_search_tools,\n",
    "    *gcal_load_and_search_tools,\n",
    "    *gcal_tools[1::],\n",
    "    *gmail_tools[1::],\n",
    "    *gsearch_tools[1::],\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a2317e0-7c64-4e73-8bca-343e3f662901",
   "metadata": {},
   "source": [
    "Now the tools are ready to pass to the agent:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03da9c28-767c-43c8-8500-7d2fc657a432",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = FunctionAgent(tools=all_tools, llm=OpenAI(model=\"gpt-4.1\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95cb5fb2-9078-47cf-b253-4a13fe2f01c5",
   "metadata": {},
   "source": [
    "## Interacting with the Agent\n",
    "\n",
    "We are now ready to interact with the Agent and test the calendar, email and search capabilities! Let's try out the search first:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e0ff83ed-e7ff-480e-8816-71adeb43222f",
   "metadata": {},
   "outputs": [],
   "source": [
    "await agent.run(\n",
    "    \"search google and find the email address for a dentist in toronto near bloor and\"\n",
    "    \" dufferin\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "368ae63d-5ee0-480e-bb1d-62ac479ecd52",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "We were able to use the google search, email and calendar tools to find a dentist and request an appointment, making sure any calendar conflicts were avoided. This notebook should prove useful for experimenting with more complicated agents, combining together the tools available in LlamaHub to create more complicated workflows the agent can execute.\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "llama_hub",
   "language": "python",
   "name": "llama_hub"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
